From 1fed6d003dd05efabe38bf5bb8ee3de02fe0481a Mon Sep 17 00:00:00 2001 From: Derk-Jan Hartman Date: Sun, 16 Jun 2019 15:53:37 +0200 Subject: [PATCH] Tablesorter: Use localeCompare This should reduce the need for the collationRegex. It also removes the need for the lowercase based sorting, and lowercase vs. uppercase order is now determined by the selected locale. Android Webviews don't support this, so fallback to the old sort routine is required. Have not removed collation regex yet, as the level of support for specific locales is not entirely clear to me. Maybe something we should gather statistics for. Bug: T32674 Bug: T47070 Change-Id: Ie7fe7317471eb12d6a65dfe3c68858c7dc0e92dc --- .../jquery.tablesorter/jquery.tablesorter.js | 52 ++++++++++++++----- .../jquery/jquery.tablesorter.parsers.test.js | 6 +-- .../jquery/jquery.tablesorter.test.js | 41 +++++++++++++-- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/resources/src/jquery.tablesorter/jquery.tablesorter.js b/resources/src/jquery.tablesorter/jquery.tablesorter.js index c1b83fde61..0c28b8e369 100644 --- a/resources/src/jquery.tablesorter/jquery.tablesorter.js +++ b/resources/src/jquery.tablesorter/jquery.tablesorter.js @@ -480,25 +480,37 @@ } function sortText( a, b ) { - return ( ( a < b ) ? -1 : ( ( a > b ) ? 1 : 0 ) ); + return ts.collator.compare( a, b ); } - function sortTextDesc( a, b ) { - return ( ( b < a ) ? -1 : ( ( b > a ) ? 1 : 0 ) ); + function sortNumeric( a, b ) { + return ( ( a < b ) ? -1 : ( ( a > b ) ? 1 : 0 ) ); } function multisort( table, sortList, cache ) { var i, - sortFn = []; + sortFn = [], + parsers = $( table ).data( 'tablesorter' ).config.parsers; for ( i = 0; i < sortList.length; i++ ) { - sortFn[ i ] = ( sortList[ i ][ 1 ] ) ? sortTextDesc : sortText; + // Android doesn't support Intl.Collator + if ( window.Intl && Intl.Collator && parsers[ sortList[ i ][ 0 ] ].type === 'text' ) { + sortFn[ i ] = sortText; + } else { + sortFn[ i ] = sortNumeric; + } } cache.normalized.sort( function ( array1, array2 ) { var i, col, ret; for ( i = 0; i < sortList.length; i++ ) { col = sortList[ i ][ 0 ]; - ret = sortFn[ i ].call( this, array1[ col ], array2[ col ] ); + if ( sortList[ i ][ 1 ] ) { + // descending + ret = sortFn[ i ].call( this, array2[ col ], array1[ col ] ); + } else { + // ascending + ret = sortFn[ i ].call( this, array1[ col ], array2[ col ] ); + } if ( ret !== 0 ) { return ret; } @@ -726,7 +738,7 @@ } } - function buildCollationTable() { + function buildCollation() { var key, keys = []; ts.collationTable = mw.config.get( 'tableSorterCollation' ); ts.collationRegex = null; @@ -739,6 +751,14 @@ ts.collationRegex = new RegExp( keys.join( '|' ), 'ig' ); } } + if ( window.Intl && Intl.Collator ) { + ts.collator = new Intl.Collator( [ + mw.config.get( 'wgPageContentLanguage' ), + mw.config.get( 'wgUserLanguage' ) + ], { + numeric: true + } ); + } } function cacheRegexs() { @@ -879,7 +899,7 @@ // may customize tableSorterCollation but load after $.ready(), other // scripts may call .tablesorter() before they have done the // tableSorterCollation customizations. - buildCollationTable(); + buildCollation(); // Legacy fix of .sortbottoms // Wrap them inside a tfoot (because that's what they actually want to be) @@ -1084,7 +1104,7 @@ buildTransformTable(); buildDateTable(); cacheRegexs(); - buildCollationTable(); + buildCollation(); return getParserById( id ); }, @@ -1110,12 +1130,20 @@ }, format: function ( s ) { var tsc; - s = s.toLowerCase().trim(); + s = s.trim(); if ( ts.collationRegex ) { tsc = ts.collationTable; s = s.replace( ts.collationRegex, function ( match ) { - var r = tsc[ match ] ? tsc[ match ] : tsc[ match.toUpperCase() ]; - return r.toLowerCase(); + var r, + upper = match.toUpperCase(), + lower = match.toLowerCase(); + if ( upper === match && !lower === match ) { + r = tsc[ lower ] ? tsc[ lower ] : tsc[ upper ]; + r = r.toUpperCase(); + } else { + r = tsc[ match.toLowerCase() ]; + } + return r; } ); } return s; diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js index 4731b32aa4..506b25bbef 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.parsers.test.js @@ -74,9 +74,9 @@ } text = [ - [ 'Mars', true, 'mars', 'Simple text' ], - [ 'Mẘas', true, 'mẘas', 'Non ascii character' ], - [ 'A sentence', true, 'a sentence', 'A sentence with space chars' ] + [ 'Mars', true, 'Mars', 'Simple text' ], + [ 'Mẘas', true, 'Mẘas', 'Non ascii character' ], + [ 'A sentence', true, 'A sentence', 'A sentence with space chars' ] ]; parserTest( 'Textual keys', 'text', text ); diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js index bd6ee166a0..f5de3aee44 100644 --- a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js +++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js @@ -64,21 +64,43 @@ [ 'Günther' ], [ 'Peter' ], [ 'Björn' ], + [ 'ä' ], + [ 'z' ], [ 'Bjorn' ], + [ 'BjÖrn' ], + [ 'apfel' ], [ 'Apfel' ], [ 'Äpfel' ], [ 'Strasse' ], [ 'Sträßschen' ] ], - umlautWordsSorted = [ + umlautWordsSortedEn = [ + [ 'ä' ], [ 'Äpfel' ], + [ 'apfel' ], [ 'Apfel' ], [ 'Björn' ], + [ 'BjÖrn' ], [ 'Bjorn' ], [ 'Günther' ], [ 'Peter' ], [ 'Sträßschen' ], - [ 'Strasse' ] + [ 'Strasse' ], + [ 'z' ] + ], + umlautWordsSortedSv = [ + [ 'apfel' ], + [ 'Apfel' ], + [ 'Bjorn' ], + [ 'Björn' ], + [ 'BjÖrn' ], + [ 'Günther' ], + [ 'Peter' ], + [ 'Strasse' ], + [ 'Sträßschen' ], + [ 'z' ], + [ 'ä' ], // ä sorts after z in Swedish + [ 'Äpfel' ] ], // Data set "digraph" @@ -702,7 +724,7 @@ 'Accented Characters with custom collation', [ 'Name' ], umlautWords, - umlautWordsSorted, + umlautWordsSortedEn, function ( $table ) { mw.config.set( 'tableSorterCollation', { ä: 'ae', @@ -716,6 +738,19 @@ } ); + tableTest( + 'Accented Characters Swedish locale', + [ 'Name' ], + umlautWords, + umlautWordsSortedSv, + function ( $table ) { + mw.config.set( 'wgPageContentLanguage', 'sv' ); + + $table.tablesorter(); + $table.find( '.headerSort:eq(0)' ).trigger( 'click' ); + } + ); + tableTest( 'Digraphs with custom collation', [ 'City' ], -- 2.20.1